package jim.org;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedList;

import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.ChatManager;
import org.jivesoftware.smack.ChatManagerListener;
import org.jivesoftware.smack.Connection;
import org.jivesoftware.smack.ConnectionConfiguration;
import org.jivesoftware.smack.Roster;
import org.jivesoftware.smack.RosterEntry;
import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException;

import util.Attributes;
import util.Profile;

/**
 * @author Jonas Dietz and Ramon Victor from the Cologne University of Applied Sciences
 * @since 2012
 * This is the main class.
 */
public class ContactList {

    private JFrame frmJim;
    private JTree treeContactList;
    private DefaultMutableTreeNode root;
    private DefaultMutableTreeNode childProtocol;
    private JMenuBar menuBar;
    private JMenu mnFile;
    private JMenu mnSettings;
    private JMenu mnContacts;
    private JMenu mnProfiles;
    private JMenu mnAbout;
    private JMenuItem mntmClose;
    private JMenuItem mntmCreateProfile;
    private JMenuItem mntmConnectProfiles;
    private JMenuItem mntmPreferences;
    private JMenuItem mntmRemoveContact;
    private JMenuItem mntmAddContact;

    private LinkedList<Connection> con = new LinkedList<Connection>();
    private Collection<RosterEntry> rosterEntries;
    private RosterEntry rosterEntry;
    public static ChatManager chatmanager;
    public static Roster roster;
    public static ArrayList<String> currentsConversations = new ArrayList<String>();
    public static LinkedList<Profile> profiles = new LinkedList<Profile>();
    private LinkedList<Profile> connected = new LinkedList<Profile>();

    /**
     * Create the application.
     */
    public ContactList() {
	initialize();
    }

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
	EventQueue.invokeLater(new Runnable() {
	    @Override
	    public void run() {
		try {
		    ContactList window = new ContactList();
		    window.frmJim.setVisible(true);
		} catch (Exception e) {
		    e.printStackTrace();
		}
	    }
	});
    }

    /**
     * Save the profile to a file.
     */
    public static void saveProfiles(){
	FileWriter writer;
	File file;
	try {
	    file = new File("profiles.jimp");
	    writer = new FileWriter(file);
	    for(int i = 0; i < profiles.size(); i++){
		Profile prof = profiles.get(i);
		writer.write("" + prof.getProtocol() + "_" + prof.getUser());
		writer.write(System.getProperty("line.separator"));
	    }
	    writer.flush();
	    writer.close();		
	} catch (Exception e) {
	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("cantSaveProfile"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
	}
    }

    /**
     * Reconnect to the server, load the chatmanager and the roster.
     */
    private void autoConnect(){
	try {
	    BufferedReader in = new BufferedReader(new FileReader("profiles.jimp"));
	    String line = null;
	    Profile profile = new Profile();
	    line = in.readLine();
	    while(line != null){
		profile = profile.loadProfile(line);
		if(!connected.contains(profile)) connect(profile);
		else connected.add(profile);
		line = in.readLine();
		loadRoster(profile.getUser());
		loadChatmanager();
	    }
	    in.close();
	} catch (FileNotFoundException e) {
//	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("cantFindFile"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
	} catch (IOException e) {
	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("ioException"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
	}
    }

    /**
     * Get the connection and the chatmanager.
     * @param profile Profile to connect to.
     */
    public void connect(Profile profile) {
	con.add(new XMPPConnection(new ConnectionConfiguration(profile.getServer(), profile.getPort(), profile.getDomain())));
	chatmanager = con.getLast().getChatManager();
	try {
	    con.getLast().connect();
	    con.getLast().login(profile.getUser(), connectPassword(profile));
	} catch (XMPPException e) {
	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("conError"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
	}
    }

    /**
     * Get the saved Password.
     * @param profile Name of the profile.
     * @return Returns the password for the profile.
     */
    private String connectPassword(Profile profile) {
	String passwd = "";

	if (profile.getPassword().length <= 1) passwd = JOptionPane.showInputDialog(Attributes.rs.getString("enterPassword"));
	else{
	    for(int i = 0; i < profile.getPassword().length; i++){
		passwd = passwd + profile.getPassword()[i];
	    }
	}
	return passwd;
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
	frmJim = new JFrame();
	frmJim.setTitle(Attributes.rs.getString("jimTitle"));
	frmJim.setBounds(100, 100, 284, 502);
	frmJim.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

	Attributes.loadAttributes();

	treeContactList = new JTree(new DefaultMutableTreeNode(Attributes.rs.getString("contacts")));
	treeContactList.addMouseListener(new TreeContactListMouseListener());
	frmJim.getContentPane().add(treeContactList, BorderLayout.CENTER);
	treeContactList.setBackground(Attributes.backgroundColor);
	treeContactList.setForeground(Attributes.textColor);
	treeContactList.setCellRenderer(new JimTreeRenderer());

	menuBar = new JMenuBar();
	frmJim.setJMenuBar(menuBar);
	menuBar.setBackground(Attributes.backgroundColor);

	mnFile = new JMenu(Attributes.rs.getString("file"));
	menuBar.add(mnFile);
	mnFile.setBackground(Attributes.backgroundColor);
	mnFile.setForeground(Attributes.textColor);

	mntmClose = new JMenuItem(Attributes.rs.getString("close"));
	mntmClose.addMouseListener(new MntmCloseMouseListener());
	mnFile.add(mntmClose);
	mntmClose.setBackground(Attributes.backgroundColor);
	mntmClose.setForeground(Attributes.textColor);

	mnSettings = new JMenu(Attributes.rs.getString("settings"));
	menuBar.add(mnSettings);
	mnSettings.setBackground(Attributes.backgroundColor);
	mnSettings.setForeground(Attributes.textColor);

	mntmPreferences = new JMenuItem(Attributes.rs.getString("preferences"));
	mntmPreferences.addMouseListener(new MntmPreferencesMouseListener());
	mnSettings.add(mntmPreferences);
	mntmPreferences.setBackground(Attributes.backgroundColor);
	mntmPreferences.setForeground(Attributes.textColor);

	mnContacts = new JMenu(Attributes.rs.getString("contacts"));
	menuBar.add(mnContacts);
	mnContacts.setBackground(Attributes.backgroundColor);
	mnContacts.setForeground(Attributes.textColor);

	mntmRemoveContact = new JMenuItem(Attributes.rs.getString("removeContact"));
	mntmRemoveContact.addMouseListener(new MntmRemoveContactMouseListener());

	mntmAddContact = new JMenuItem(Attributes.rs.getString("addContact"));
	mntmAddContact.addMouseListener(new MntmAddContactMouseListener());
	mnContacts.add(mntmAddContact);
	mntmAddContact.setBackground(Attributes.backgroundColor);
	mntmAddContact.setForeground(Attributes.textColor);
	mntmRemoveContact.setBackground(Attributes.backgroundColor);
	mntmRemoveContact.setForeground(Attributes.textColor);
	mnContacts.add(mntmRemoveContact);

	mnProfiles = new JMenu(Attributes.rs.getString("profiles"));
	menuBar.add(mnProfiles);
	mnProfiles.setBackground(Attributes.backgroundColor);
	mnProfiles.setForeground(Attributes.textColor);

	mntmCreateProfile = new JMenuItem(Attributes.rs.getString("createNewProfile"));
	mntmCreateProfile.addMouseListener(new MntmCreateProfileMouseListener());
	mnProfiles.add(mntmCreateProfile);
	mntmCreateProfile.setBackground(Attributes.backgroundColor);
	mntmCreateProfile.setForeground(Attributes.textColor);

	mntmConnectProfiles = new JMenuItem(Attributes.rs.getString("connectProfiles"));
	mntmConnectProfiles.addMouseListener(new MntmConnectProfilesMouseListener());
	mnProfiles.add(mntmConnectProfiles);
	mntmConnectProfiles.setBackground(Attributes.backgroundColor);
	mntmConnectProfiles.setForeground(Attributes.textColor);

	mnAbout = new JMenu(Attributes.rs.getString("about"));
	mnAbout.addMouseListener(new MnAboutMouseListener());
	menuBar.add(mnAbout);
	mnAbout.setBackground(Attributes.backgroundColor);
	mnAbout.setForeground(Attributes.textColor);

	loadProfiles();
	autoConnect();
    }

    /**
     * This will load the chatmanager for this connection. 
     */
    private void loadChatmanager() {
	chatmanager.addChatListener(new ChatManagerListener() {
	    @Override
	    public void chatCreated(Chat chat, boolean createdLocally) {
		if (!createdLocally) {
		    rosterEntry = roster.getEntry(chat.getParticipant().split("/")[0]);
		    new MessageWindows(rosterEntry, chat.getThreadID());
		}
	    }
	});
    }

    /**
     * Load the profile from a file.
     */
    private void loadProfiles(){
	try {
	    BufferedReader in = new BufferedReader(new FileReader("profiles.jimp"));
	    String line = null;
	    Profile profile = new Profile();
	    line = in.readLine();
	    while(line != null){
		profile = profile.loadProfile(line);
		profiles.add(profile);
		line = in.readLine();
	    }
	    in.close();
	} catch (FileNotFoundException e) {
//	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("cantFindFile"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
	} catch (IOException e) {
	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("ioException"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
	}
    }

    /**
     * Get the roster from the server and add this to the contactlist.
     * @param profile Name of the profile.
     */
    private void loadRoster(String profile) {
	roster = con.getLast().getRoster();
	root = (DefaultMutableTreeNode) treeContactList.getModel().getRoot();
	childProtocol = new DefaultMutableTreeNode(profile);
	root.add(childProtocol);
	rosterEntries = roster.getEntries();
	for (RosterEntry entry : rosterEntries) {
	    childProtocol.add(new DefaultMutableTreeNode(entry));
	}
	treeContactList.setModel(new DefaultTreeModel(root));
    }

    /**
     * A custom TreeCellRenderer so that the Tree will look like we want it to.
     */
    @SuppressWarnings("serial")
    private class JimTreeRenderer extends DefaultTreeCellRenderer {
	@Override
	public Color getBackgroundNonSelectionColor() {
	    return Attributes.backgroundColor;
	}
	@Override
	public Color getTextNonSelectionColor() {
	    return Attributes.textColor;
	}
    }

    /**
     * Open the "About" frame.
     */
    private class MnAboutMouseListener extends MouseAdapter {
	@Override
	public void mouseClicked(MouseEvent arg0) {
	    JOptionPane.showMessageDialog(null, Attributes.rs.getString("aboutBody"));
	}
    }

    /**
     * Adds a contact to the profile.
     */
    private class MntmAddContactMouseListener extends MouseAdapter {
	@Override
	public void mouseReleased(MouseEvent arg0) {
	    String addEntry = JOptionPane.showInputDialog(Attributes.rs.getString("addContactDialogBody"));
	    if (addEntry!="") {
		try {
		    roster.createEntry(addEntry, addEntry, null);
		    JOptionPane.showMessageDialog(null, Attributes.rs.getString("contactAdded"));
		} catch (XMPPException e) {
		    JOptionPane.showMessageDialog(null, Attributes.rs.getString("cantAddContact"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
		}
	    }
	}
    }

    /**
     * Simple close event. Nothing special.
     */
    private class MntmCloseMouseListener extends MouseAdapter {
	@Override
	public void mouseReleased(MouseEvent arg0) {
	    frmJim.setVisible(false);
	    frmJim.setEnabled(false);
	    frmJim.dispose();
	}
    }

    /**
     * Reload the roster. But this is a little buggy.
     */
    private class MntmConnectProfilesMouseListener extends MouseAdapter {
	@Override
	public void mousePressed(MouseEvent e) {
	    autoConnect();
	}
    }

    /**
     * Starts the "Create profile" frame.
     */
    private class MntmCreateProfileMouseListener extends MouseAdapter {
	@Override
	public void mouseReleased(MouseEvent e) {
	    NewProfile.run();
	}
    }

    /**
     * Starts the "Preferences" frame.
     */
    private class MntmPreferencesMouseListener extends MouseAdapter {
	@Override
	public void mouseReleased(MouseEvent e) {
	    Preferences.run();
	}
    }

    /**
     * Deletes the selected contact.
     */
    private class MntmRemoveContactMouseListener extends MouseAdapter {
	@Override
	public void mouseReleased(MouseEvent arg0) {
	    DefaultMutableTreeNode tmpNode = (DefaultMutableTreeNode) treeContactList.getLastSelectedPathComponent();
	    if (tmpNode!=null) {
		RosterEntry tmpEntry = (RosterEntry) tmpNode.getUserObject();
		try {
		    int option = JOptionPane.showConfirmDialog(null, Attributes.rs.getString("areYouSure"), Attributes.rs.getString("deletingContact"), JOptionPane.OK_CANCEL_OPTION);
		    if (option == JOptionPane.YES_OPTION) roster.removeEntry(tmpEntry);
		    JOptionPane.showMessageDialog(null, Attributes.rs.getString("restartJIM"), Attributes.rs.getString("restartRequired"), JOptionPane.INFORMATION_MESSAGE);
		} catch (XMPPException e) {
		    JOptionPane.showMessageDialog(null, Attributes.rs.getString("cantDeleteContact"), Attributes.rs.getString("error"), JOptionPane.ERROR_MESSAGE);
		}
	    }
	}
    }

    /**
     * Open a chat window with the selected contact. Only at doubleclick!
     */
    private class TreeContactListMouseListener extends MouseAdapter {
	@Override
	public void mouseClicked(MouseEvent e) {
	    int selRow = treeContactList.getRowForLocation(e.getX(), e.getY());
	    if (selRow!=-1) {
		if (e.getClickCount()==2) {
		    DefaultMutableTreeNode node = (DefaultMutableTreeNode) treeContactList.getLastSelectedPathComponent();
		    RosterEntry rosterEntry = (RosterEntry) node.getUserObject();
		    if (!currentsConversations.contains(rosterEntry.getUser())) {
			new MessageWindows(rosterEntry, chatmanager.createChat(rosterEntry.getUser(), null).getThreadID());
			System.out.println(currentsConversations.contains(rosterEntry.getName()));
			currentsConversations.add(rosterEntry.getUser());
		    }
		    treeContactList.setSelectionPath(null);
		}
	    }
	}
    }
}